package com.atguigu.gmall.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.cart.client.CartFeignClient;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.service.RabbitService;
import com.atguigu.gmall.common.util.HttpClientUtil;
import com.atguigu.gmall.model.enums.OrderStatus;
import com.atguigu.gmall.model.enums.PaymentWay;
import com.atguigu.gmall.model.enums.ProcessStatus;
import com.atguigu.gmall.model.order.OrderDetail;
import com.atguigu.gmall.model.order.OrderInfo;
import com.atguigu.gmall.order.mapper.OrderDetailMapper;
import com.atguigu.gmall.order.mapper.OrderInfoMapper;
import com.atguigu.gmall.order.service.OrderService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author atguigu-mqx
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderInfoMapper,OrderInfo> implements OrderService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private CartFeignClient cartFeignClient;

    @Autowired
    private RabbitService rabbitService;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long saveOrderInfo(OrderInfo orderInfo) {
        //  orderInfo 【total_amount order_status user_id payment_way out_trade_no trade_body
        //  operate_time expire_time process_status 】
        //  计算总金额：
        orderInfo.sumTotalAmount();
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());
        orderInfo.setPaymentWay(PaymentWay.ONLINE.name());
        //  第三方交易编号：out_trade_no 对接支付用的！ 不能重复
        //  String outTradeNo = UUID.randomUUID().toString();
        String outTradeNo = "ATGUIGU"+System.currentTimeMillis()+new Random().nextInt(1000);
        //  String outTradeNo = "ATGUIGU"+System.currentTimeMillis()+UUID.randomUUID().toString();
        orderInfo.setOutTradeNo(outTradeNo);
        //  trade_body 订单的描述：
        orderInfo.setTradeBody("红旗电商公司收费");
        //  将用户购买的商品名称拼接起来！
        orderInfo.setOperateTime(new Date());
        //  订单过期时间！
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE,1);
        //  当前系统时间+1天
        orderInfo.setExpireTime(calendar.getTime());
        //  process_status
        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());
        orderInfoMapper.insert(orderInfo);

        //  orderDetail 获取数据
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        //  循环遍历
        for (OrderDetail orderDetail : orderDetailList) {
            orderDetail.setOrderId(orderInfo.getId());
            orderDetailMapper.insert(orderDetail);
        }

        //  删除购物车数据：
        //  this.cartFeignClient.delCart(orderInfo.getUserId().toString());

        Long orderId = orderInfo.getId();
        //  发送一个延迟消息！ 按照代码来写，延迟时间1天;  为了测试给3秒钟！
        //  发送的内容根据消费者的业务！
        this.rabbitService.sendDelayMessage(MqConst.EXCHANGE_DIRECT_ORDER_CANCEL,MqConst.ROUTING_ORDER_CANCEL,orderId,MqConst.DELAY_TIME);
        //  返回数据！
        return orderId;
    }

    @Override
    public String getTradeNo(String userId) {
        //  定义一个流水号
        String tradeNo = UUID.randomUUID().toString();
        //  定义一个key！
        String tradeNoKey = "tradeNo:"+userId;
        //  保存数据
        this.redisTemplate.opsForValue().set(tradeNoKey,tradeNo);
        return tradeNo;
    }

    @Override
    public Boolean checkTradeNo(String tradeNo, String userId) {
        //  定义一个key！
        String tradeNoKey = "tradeNo:"+userId;
        //  获取缓存的流水号
        String redisTradeNo = (String) this.redisTemplate.opsForValue().get(tradeNoKey);
        //  返回比较结果
        return tradeNo.equals(redisTradeNo);
    }

    @Override
    public void delTradeNo(String userId) {
        //  定义一个key！
        String tradeNoKey = "tradeNo:"+userId;
        //  保存数据
        this.redisTemplate.delete(tradeNoKey);

    }

    @Override
    public Boolean checkStock(Long skuId, Integer skuNum) {
        //  如何调用验证库存? httpclient
        String result = HttpClientUtil.doGet("http://localhost:9001/hasStock?skuId=" + skuId + "&num=" + skuNum);
        //  判断并返回
        return "1".equals(result);
    }

    @Override
    public IPage getPage(Page<OrderInfo> orderInfoPage, String userId) {
        /*
            根据业务分析应该查询两张表的数据： orderInfo ,orderDetail;
         */
        IPage<OrderInfo> iPage = orderInfoMapper.selectOrderByPage(orderInfoPage,userId);
        //  赋值订单状态
        iPage.getRecords().forEach(orderInfo -> {
            orderInfo.setOrderStatusName(OrderStatus.getStatusNameByStatus(orderInfo.getOrderStatus()));
        });

        //  返回数据
        return iPage;
    }

    @Override
    public void execExpiredOrder(Long orderId) {
        //  本质更新状态！
        //        OrderInfo orderInfo = new OrderInfo();
        //        orderInfo.setId(orderId);
        //        //  更新的内容：
        //        orderInfo.setOrderStatus(OrderStatus.CLOSED.name());
        //        orderInfo.setProcessStatus(ProcessStatus.CLOSED.name());
        //
        //        //  更新数据：
        //        orderInfoMapper.updateById(orderInfo);
        this.updateOrderStatus(orderId, ProcessStatus.CLOSED);

        //  发送一个消息关闭交易记录！
        this.rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE,MqConst.ROUTING_PAYMENT_CLOSE,orderId);

    }

    //  后续会有很多地方，需要更新状态的！ 有关闭，有支付成功，有等待发货！
    @Override
    public void updateOrderStatus(Long orderId, ProcessStatus processStatus) {
        //  更新数据！
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setId(orderId);
        //  更新的内容： 进度的状态中能够获取订单的状态！
        orderInfo.setOrderStatus(processStatus.getOrderStatus().name());
        orderInfo.setProcessStatus(processStatus.name());

        //  更新数据：
        orderInfoMapper.updateById(orderInfo);
    }

    @Override
    public OrderInfo getOrderInfo(Long orderId) {
        OrderInfo orderInfo = this.orderInfoMapper.selectById(orderId);
        //  判断
        if (orderInfo!=null){
            //  获取到订单明细
            List<OrderDetail> orderDetailList = this.orderDetailMapper.selectList(new QueryWrapper<OrderDetail>().eq("order_id", orderId));
            orderInfo.setOrderDetailList(orderDetailList);
        }
        //  返回订单对象！
        return orderInfo;
    }

    @Override
    public void sendMsgToWare(Long orderId) {
        //  修改订单状态！
        this.updateOrderStatus(orderId,ProcessStatus.NOTIFIED_WARE);
        //  根据订单Id 来获取订单主表数据，订单明细表数据！
        OrderInfo orderInfo = this.getOrderInfo(orderId);

        //  需要将orderInfo 中的部分属性字段组成一个map ，最后将map 转换为json 数据发送给库存！
        Map map = this.initWare(orderInfo);

        //  发送字符串给库存！
        this.rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_WARE_STOCK,MqConst.ROUTING_WARE_STOCK, JSON.toJSONString(map));
    }

    /**
     * 将orderInfo 部分数据转换为Map 集合
     * @param orderInfo
     * @return
     */
    public Map initWare(OrderInfo orderInfo) {
        //  声明一个map 集合
        HashMap<String, Object> map = new HashMap<>();
        //  给map 赋值！
        map.put("orderId",orderInfo.getId());
        map.put("consignee",orderInfo.getConsignee());
        map.put("consigneeTel",orderInfo.getConsigneeTel());
        map.put("orderComment",orderInfo.getOrderComment());
        map.put("orderBody",orderInfo.getTradeBody());
        map.put("deliveryAddress",orderInfo.getDeliveryAddress());
        map.put("paymentWay","2");
        map.put("wareId",orderInfo.getWareId()); // 拆单要使用！
        //  先获取到订单明细集合
        List<HashMap<String, Object>> list = orderInfo.getOrderDetailList().stream().map(orderDetail -> {
            HashMap<String, Object> detailMap = new HashMap<>();
            //  把数据添加到map 集合
            detailMap.put("skuId", orderDetail.getSkuId());
            detailMap.put("skuNum", orderDetail.getSkuNum());
            detailMap.put("skuName", orderDetail.getSkuName());
            //  返回数据
            return detailMap;
        }).collect(Collectors.toList());

        map.put("details",list);
        return map;
    }

    @Override
    public List<OrderInfo> orderSplit(String orderId, String wareSkuMap) {
        //  声明子订单集合！
        List<OrderInfo> subOrderInfoList = new ArrayList<>();
        /*
        1.  需要知道原始订单是谁！ 哪个订单要被拆分！
        2.  wareSkuMap 对它进行解析 [{"wareId":"1","skuIds":["2","10"]},{"wareId":"2","skuIds":["3"]}]
        3.  创建子订单并给子订单进行赋值！
        4.  调用saveOrderInfo 方法将子订单保存到orderInfo 同时也有orderDetail;
        5.  需要将子订单对象添加到子订单集合中！
        6.  改变原始订单状态！
         */
        //  原始订单
        OrderInfo originOrderInfo = this.getOrderInfo(Long.parseLong(orderId));
        //  解析wareSkuMap
        List<Map> mapList = JSON.parseArray(wareSkuMap, Map.class);
        //  判断
        if (!CollectionUtils.isEmpty(mapList)){
            //  循环遍历获取里面的数据！
            for (Map map : mapList) {
                String wareId = (String) map.get("wareId");
                List<String> skuIdList = (List<String>) map.get("skuIds");
                //  创建新的子订单！
                OrderInfo subOrderInfo = new OrderInfo();
                //  属性赋值：
                BeanUtils.copyProperties(originOrderInfo,subOrderInfo);
                //  保证id 不能重复！
                subOrderInfo.setId(null);
                //  赋值parentOrderId
                subOrderInfo.setParentOrderId(Long.parseLong(orderId));
                //  赋值仓库Id
                subOrderInfo.setWareId(wareId);
                //  重新计算金额: 跟订单明细有关系！
                List<OrderDetail> orderDetails = new ArrayList<>();
                List<OrderDetail> orderDetailList = originOrderInfo.getOrderDetailList();
                //  循环遍历
                for (OrderDetail orderDetail : orderDetailList) {
                    for (String skuId : skuIdList) {
                        //  1号仓库对应的订单明细 2,10;
                        if (orderDetail.getSkuId()==Long.parseLong(skuId)){
                            orderDetails.add(orderDetail);
                        }
                    }
                }
                //  赋值子订单明细
                subOrderInfo.setOrderDetailList(orderDetails);
                //  计算子订单的总金额！
                subOrderInfo.sumTotalAmount();
                //  保存子订单：
                this.saveOrderInfo(subOrderInfo);
                //  将子订单数据添加到集合中！
                subOrderInfoList.add(subOrderInfo);
            }
        }
        //  更改原始订单状态！
        this.updateOrderStatus(Long.parseLong(orderId),ProcessStatus.SPLIT);
        //  返回子订单集合！
        return subOrderInfoList;
    }

    @Override
    public void execExpiredOrder(Long orderId, String flag) {
        //        orderInfoMapper.updateById(orderInfo);
        this.updateOrderStatus(orderId, ProcessStatus.CLOSED);
        if ("2".equals(flag)){
            //  发送一个消息关闭交易记录！
            this.rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE,MqConst.ROUTING_PAYMENT_CLOSE,orderId);
        }

    }
}
